#include "public.h"
#include "tools.h"
#include "scan.h"
#include "parser.h"

/* static variable */
static token_type cur_token;         /* save current token */

/* local function */
static parser_tree *fun_define(void);
static parser_tree *compound_stmt(void);
static void vare_defines(void);
static parser_tree *statement_list(void);
static parser_tree *statement(void);
static parser_tree *multi_stmt(void);
static parser_tree *if_ss(void);
static parser_tree *while_ss(void);
static parser_tree *in_ss(void);
static parser_tree *out_ss(void);
static parser_tree *expression(void);
static parser_tree *simple_expression(void);
static parser_tree *add_expression(void);
static parser_tree *term(void);
static parser_tree *factor(void);

/* global function */
parser_tree *parser(void);

/* function define */
static void match(token_type token)
{
    if(cur_token == token)
		cur_token = get_token();
	else
	    fprintf(syn_point, "syntax error at line:%d\ttoken:%s\n", lineno, token_str);
}

parser_tree *fun_define(void)
{
    parser_tree *tree = NULL;
	match(MAIN);
	match(LPARE);
	match(RPARE);
	tree = compound_stmt();
	return tree;
}

parser_tree *compound_stmt(void)
{
    parser_tree *tree = NULL;
	match(LBRA);
	vare_defines();
	tree = statement_list();
	match(RBRA);
	return tree;
}

void vare_defines(void)
{
    while(cur_token == INT)
	{
	    match(INT);
		if(find_tab(token_str) >= 0)
			fprintf(syn_point, "syntax error at line:%d,redefine variable %s \n", lineno, token_str);
		insert_tab(token_str);
		match(ID);
		match(SEMI);
	}
}

parser_tree *statement_list(void)
{
	parser_tree *tree = NULL;
	parser_tree *p = NULL, *q = NULL;
	while((cur_token == IF)||(cur_token == WHILE)||(cur_token == IN)||(cur_token == OUT)||(cur_token == ID)||(cur_token == SEMI))
	{
	    p = statement();
		if(p != NULL)
		{
		    if(tree == NULL)tree = q = p;
			else
			{
			    q -> next = p;
				q = p;
			}
		}
	}
    return tree;
}

parser_tree *statement(void)
{
    parser_tree *tree = NULL;
	switch(cur_token)
	{
    	case SEMI:
	        tree = malloc_stmt(null_s);
	     	match(SEMI);
	    	return tree;
	    	/* break;  */
	    case IF :
			tree = if_ss();break;
	    case WHILE :
			tree = while_ss();break;
		case IN:
			tree = in_ss();break;
		case OUT:
			tree = out_ss();break;
		case ID:
			tree = expression();break;
		case LBRA:
			tree = multi_stmt();break;
		default:
			fprintf(syn_point, "syntax error at the line:%d\ttoken:%s\n", lineno, token_str);break;
	}
	return tree;
}

parser_tree *multi_stmt(void)
{
    parser_tree *tree = NULL;
	parser_tree *p = NULL, *q = NULL;
	match(LBRA);
	while((cur_token == IF)||(cur_token == WHILE)||(cur_token == IN)||(cur_token == OUT)||(cur_token == ID)||(cur_token == SEMI))
	{
	    p = statement();
		if(p != NULL)
		{
		    if(tree == NULL)tree = q = p;
			else
			{
			    q -> next = p;
				q = p;
			}
		}/* end if */
	}/* end while */
	match(RBRA);
	return tree;
}

parser_tree *if_ss(void)
{
    parser_tree *tree;
	match(IF);
	match(LPARE);
	tree = malloc_stmt(if_s);
	if(tree != NULL)
		tree -> child[0] = simple_expression();
	match(RPARE);
    if(tree != NULL)
		tree -> child[1] = statement();
	if(cur_token == ELSE)
	{
	    match(ELSE);
		if(tree != NULL);
		    tree -> child[2] = statement();
	}
	return tree;
}

parser_tree *while_ss(void)
{
    parser_tree *tree;
	match(WHILE);
	match(LPARE);
	tree = malloc_stmt(while_s);
	if(tree != NULL)
        tree -> child[0] = simple_expression();
	match(RPARE);
	if(tree != NULL)
		tree -> child[1] = statement();
	return tree;
}

parser_tree *in_ss(void)
{
    parser_tree *tree;
	match(IN);
	match(LPARE);
	tree = malloc_stmt(in_s);
	if((tree != NULL)&&(cur_token == ID))
		(tree -> attribute).name = str_cpy(token_str);
	match(ID);
    match(RPARE);
	match(SEMI);
	return tree;
}

parser_tree *out_ss(void)
{
    parser_tree *tree;
	match(OUT);
	match(LPARE);
	tree = malloc_stmt(out_s);
	if((tree != NULL)&&(cur_token == ID))
		(tree -> attribute).name = str_cpy(token_str);
	match(ID);
	match(RPARE);
	match(SEMI);
	return tree;
}

parser_tree *expression(void)
{
    parser_tree *tree;
	tree = malloc_stmt(assign_s);
	if((tree != NULL)&&(cur_token == ID))
		(tree -> attribute).name = str_cpy(token_str);
	match(ID);
	match(ASSIGN);
	if(tree != NULL)
		tree -> child[0] = simple_expression();
	match(SEMI);
	return tree;
}

parser_tree *simple_expression()
{
    parser_tree *tree;
	parser_tree *p;
	tree = add_expression();
	if((cur_token == LE)||(cur_token == LS)||(cur_token == GT)||(cur_token == GE)||(cur_token == EQ)||(cur_token == NE))
	{
	    p = malloc_exp(op_e);
		if(p != NULL)
		{
		    p -> child[0] = tree;
			(p -> attribute).op = cur_token;
			tree = p;
		}
		match(cur_token);
		if(tree != NULL)
			tree -> child[1] = add_expression();
	}
	return tree;
}

parser_tree *add_expression(void)
{
    parser_tree *tree;
	parser_tree *p;
	tree = term();
	if((cur_token == PLUS)||(cur_token == MINUS))
	{
	    p = malloc_exp(op_e);
		if(p != NULL)
		{
		    p -> child[0] = tree;
			(p -> attribute).op = cur_token;
			tree = p;
		}
		match(cur_token);
		if(tree != NULL)
			tree -> child[1] = term();
	}
	return tree;
}

parser_tree *term(void)
{
    parser_tree *tree;
	parser_tree *p;
	tree = factor();
	if((cur_token == TIMES)||(cur_token == DIVI))
	{
	    p = malloc_exp(op_e);
		if(p != NULL)
		{
		    p -> child[0] = tree;
			(p ->attribute).op = cur_token;
			tree = p;
		}
		match(cur_token);
		if(tree != NULL)
            tree -> child[1] = factor();
	}
	return tree;
}

parser_tree *factor(void)
{
    parser_tree *tree = NULL;
	switch(cur_token)
	{
	    case ID:
			tree = malloc_exp(id_e);
			if(tree != NULL)
				(tree -> attribute).name = str_cpy(token_str);
			match(ID);
			break;
		case NUM:
			tree = malloc_exp(const_e);
			if(tree != NULL)
				(tree ->attribute).value = atoi(token_str);
			match(NUM);
			break;
		case LPARE:
			match(LPARE);
			tree = simple_expression();
			match(RPARE);
			break;
		default:
			fprintf(syn_point, "error:at the line of %d\ttoken:%s\n", lineno, token_str);
			break;
	}
	return tree;
}

parser_tree *parser(void)
{
    parser_tree *tree;
	cur_token = get_token();
	tree = fun_define();
	if(cur_token != ENDFILE)
		fprintf(syn_point, "syntax error at the line:%d\ttoken:%s\n", lineno, token_str);
	return tree;
}
